package cn.b.sky.form.service.impl;

import cn.b.sky.common.exception.ImeException;
import cn.b.sky.common.utils.DateUtils;
import cn.b.sky.form.constans.CustomerPageConstans;
import cn.b.sky.form.dao.BusinessAttachmentDao;
import cn.b.sky.form.dao.BusinessFieldDao;
import cn.b.sky.form.dao.BusinessTypeDao;
import cn.b.sky.form.dao.FormObjectDao;
import cn.b.sky.form.model.BusinessAttachment;
import cn.b.sky.form.model.BusinessField;
import cn.b.sky.form.model.BusinessType;
import cn.b.sky.form.model.Screen;
import cn.b.sky.form.service.FormObjectService;
import cn.b.sky.form.service.ScreenService;
import cn.b.sky.form.service.SequenceService;
import cn.b.sky.user.SecurityContextUtil;
import cn.b.sky.user.service.NumberRuleService;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.ParseException;
import java.util.*;
import java.util.Map.Entry;

@Service("formObjectService")
public class FormObjectServiceImpl implements FormObjectService {

  private static final Logger LOGGER = LoggerFactory.getLogger(FormObjectServiceImpl.class);
  @Autowired
  private FormObjectDao formObjectDao;

  @Autowired
  private BusinessTypeDao businessTypeDao;

  @Autowired
  private BusinessFieldDao businessFieldDao;

  @Resource
  private ScreenService screenService;
  
  @Resource
  private SequenceService sequenceService;

  @Resource
  private NumberRuleService numberRuleService;
  
  @Autowired
  private BusinessAttachmentDao businessAttachmentDao;

  @Override
  public List<Map<String, Object>> getFormObjectDataByBizId(String siteCode, String tabelName, String bussinessKey) {
    if (StringUtils.isBlank(tabelName) || StringUtils.isBlank(siteCode) || StringUtils.isBlank(bussinessKey)) {
      return Collections.EMPTY_LIST;
    }
    StringBuilder sql = new StringBuilder("select * from ");
    sql.append(tabelName).append(" where ").append(CustomerPageConstans.SITECODE).append(" = ").append("#{siteCode}");
    sql.append(" and ").append(CustomerPageConstans.BUSINESSCODE).append(" = ").append("#{bussinessKey}");

    return formObjectDao.selectFormObjectByBussinessKey(sql.toString(), siteCode, bussinessKey);
  }

  @Override
  public String saveFormObjectData(Map<String, Object> params, String formKey, String siteCode) throws Exception {
    if (StringUtils.isBlank(formKey) || StringUtils.isBlank(siteCode)) {
      ImeException.newException("formkey 或者 siteCode 为空!");
    }
    Screen screen = screenService.getScreen(siteCode, formKey);
    BusinessType type = businessTypeDao.getBusinessTypeByPrimaryKey(siteCode, screen.getBusinessType());
    if (type == null || type.getTableCreateFlag() != 1) {
      throw ImeException.newException("数据库表不存在!");
    }
    if (StringUtils.isBlank(type.getDbTable())) {
      throw ImeException.newException("数据库表名为空!");
    }
    List<Map<String, Object>> tableColumns = formObjectDao.selectTableMataDatas(type.getDbTable());
    List<BusinessField> fields = businessFieldDao.selectBusinessFields(siteCode, type.getBusinessType());
    List<BusinessField> attachFields = new ArrayList<BusinessField>();
    Map<String, String[]> fileMap = new HashMap<String, String[]>();


    Long id = sequenceService.generateNextValue(siteCode, type.getSequenceCode());
    params.put(CustomerPageConstans.ID, id);
    params.put(CustomerPageConstans.BUSINESSCODE,
        numberRuleService.selectNextNumber(siteCode, type.getNumberRuleCode()));
    params.put(CustomerPageConstans.CREATEUSER, SecurityContextUtil.getCurrentUserName());
    params.put(CustomerPageConstans.CREATEDATETIME, new Date());
    params.put(CustomerPageConstans.MODIFYUSER, SecurityContextUtil.getCurrentUserName());
    params.put(CustomerPageConstans.MODIFYDATETIME, new Date());
    params.put(CustomerPageConstans.SITECODE, siteCode);
    params.put("formDataSql", getInsertSql(tableColumns, params, type.getDbTable()));
    formObjectDao.insertFormObject(params);
    if(!attachFields.isEmpty()){
      //保存附件
      this.saveBusinessAttachements(attachFields, fileMap, id);
    }
    return (String) params.get(CustomerPageConstans.BUSINESSCODE);
  }

  /**
   * 
   * <strong>Title : saveBusinessAttachements<br></strong>
   * <strong>Description : </strong>保存附件,先删除再保存<br> 
   * <strong>Create on : 2016-11-29 下午3:29:38<br></strong>
   * <p>
   * @param attachFields
   * @param fileMap
   * void
   * @throws 
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   * <strong>修改历史:</strong><br>
   * 修改人 | 修改日期 | 修改描述<br>
   * -------------------------------------------<br>
   * <br>
   * <br>
   */
  private void saveBusinessAttachements(List<BusinessField> attachFields, Map<String, String[]> fileMap, Long id) {
    for(BusinessField field : attachFields){
       if(field != null && !StringUtils.isBlank(field.getDbField())){
         String[] values = fileMap.get(field.getDbField());
         if(values != null && values.length > 0){
           this.deletBusinessAttachmentsByField(new BusinessAttachment(id, field.getSiteCode(), field.getBusinessType(), field.getDbField()));
           for(String attachStr : values){
             BusinessAttachment attachment = JSONObject.parseObject(attachStr, BusinessAttachment.class);
             attachment.setBusinessType(field.getBusinessType());
             attachment.setBusinessField(field.getDbField());
             attachment.setBusinessid(id);
             businessAttachmentDao.insertBusinessAttachment(attachment);
           }
         }
       }
    }
  }

  
  /**
   * 
   * <strong>Title : deletBusinessAttachmentsByField<br></strong>
   * <strong>Description : </strong>删除附件<br> 
   * <strong>Create on : 2016-11-29 下午3:53:41<br></strong>
   * <p>
   * @param businessAttachment
   * void
   * @throws 
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   * <strong>修改历史:</strong><br>
   * 修改人 | 修改日期 | 修改描述<br>
   * -------------------------------------------<br>
   * <br>
   * <br>
   */
  private void deletBusinessAttachmentsByField(BusinessAttachment businessAttachment) {   
    businessAttachmentDao.deleteBusinessAttachment(businessAttachment);
  }

  @Override
  public void modifyFormObjectData(Map<String, Object> params, String formKey, String siteCode) throws Exception {
    if (StringUtils.isBlank(formKey) || StringUtils.isBlank(siteCode)) {
      throw ImeException.newException("formkey 或者 siteCode 为空!");
    }
    Screen screen = screenService.getScreen(siteCode, formKey);
    if(screen == null || StringUtils.isBlank(screen.getBusinessType())){
      LOGGER.error("表单不存在或者表单没有关联业务模型!");
      return;
    }
    BusinessType type = businessTypeDao.getBusinessTypeByPrimaryKey(siteCode, screen.getBusinessType());
    if (type == null || type.getTableCreateFlag() != 1) {
      throw ImeException.newException("数据库表不存在!");
    }
    if (StringUtils.isBlank(type.getDbTable())) {
      throw ImeException.newException("数据库表名为空!");
    }
    if (!params.containsKey(CustomerPageConstans.BUSINESSCODE)) {
      throw ImeException.newException("缺少业务代码!");
    }
    List<Map<String, Object>> tableColumns = formObjectDao.selectTableMataDatas(type.getDbTable());
    List<BusinessField> fields = businessFieldDao.selectBusinessFields(siteCode, type.getBusinessType());
    List<BusinessField> attachFields = new ArrayList<BusinessField>();
    Map<String, String[]> fileMap = new HashMap<String, String[]>();
    //还原表单数据为javaType，直接利用mybatis保存数据时的数据转化机制
    for (BusinessField field : fields) {
      if (field != null) {
        if (params.containsKey(field.getDbField()) && params.get(field.getDbField()) != null) {
          if("attachment".equalsIgnoreCase(field.getFieldType())){
            attachFields.add(field);
            Object values = params.get(field.getDbField());
            if(values instanceof   String[] ){
              fileMap.put(field.getDbField(), (String[])values);
            }else{
              fileMap.put(field.getDbField(), new String[]{values.toString()});
            }
            //主表存储附件数量
            params.put(field.getDbField(), fileMap.get(field.getDbField()).length);
            continue;
          }
          params.put(field.getDbField(), convertData(params.get(field.getDbField()), field.getDataType()));
        }
      }
    }
    String bussinessKey = (String) params.get(CustomerPageConstans.BUSINESSCODE);
    if (!StringUtils.isBlank(bussinessKey)) {
      //对应mybatis配置文件里的formInserSql占位符
      List<Map<String, Object>> formDatas = this.getFormObjectDataByBizId(siteCode, type.getDbTable(), bussinessKey);
      if (formDatas != null && formDatas.size() == 1) {
        params.put(CustomerPageConstans.SITECODE, siteCode);
        params.put(CustomerPageConstans.MODIFYUSER, SecurityContextUtil.getCurrentUserName());
        params.put(CustomerPageConstans.MODIFYDATETIME, new Date());
        params.put("formDataSql", getUpdateSql(tableColumns, params, type.getDbTable()));
      } else {
        throw ImeException.newException("查询数据多余一条,表" + type.getDbTable() + "业务code为:" + bussinessKey);
      }
      formObjectDao.updateFormObject(params);
      if(!attachFields.isEmpty()){
        //保存附件
        this.saveBusinessAttachements(attachFields, fileMap, Long.valueOf(formDatas.get(0).get(CustomerPageConstans.ID).toString()));
      }
    }
  }

  private Object getUpdateSql(List<Map<String, Object>> tableColumns, Map<String, Object> params,
      String tableName) {
    //组装修改语句的前半段
    StringBuilder sql = new StringBuilder("update ");
    sql.append(tableName).append(" set ");
    for (Map<String, Object> columns : tableColumns) {
      for (Entry<String, Object> entity : columns.entrySet()) {
        Object value = entity.getValue();
        if (value == null) {
          continue;
        }
        if (!isSystemField(value.toString())
            && params.containsKey(value.toString().toUpperCase())) {
          sql.append(entity.getValue()).append(" = ");
          sql.append("#{").append(entity.getValue()).append("},");
        }
        break; //每个map就一个值
      }
    }
    sql.append(" where ").append(CustomerPageConstans.SITECODE).append(" = ").append("#{SITECODE}");
    sql.append(" and ").append(CustomerPageConstans.BUSINESSCODE).append(" = ").append("#{BUSINESSKEY}");
    return sql.toString().replaceAll(", where", " where");
  }

  /**
   * 
   * <strong>Title : isSystemField<br></strong>
   * <strong>Description : </strong>系统字段，id，siteCode，businessKey不修改<br> 
   * <strong>Create on : 2016-11-24 上午10:53:44<br></strong>
   * <p>
   * @param filed
   * @return
   * boolean
   * @throws 
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   * <strong>修改历史:</strong><br>
   * 修改人 | 修改日期 | 修改描述<br>
   * -------------------------------------------<br>
   * <br>
   * <br>
   */
  private boolean isSystemField(String filed) {
    if(CustomerPageConstans.ID.equalsIgnoreCase(filed)){
      return true;
    }
    if(CustomerPageConstans.SITECODE.equalsIgnoreCase(filed)){
      return true;
    }
    if(CustomerPageConstans.BUSINESSCODE.equalsIgnoreCase(filed)){
      return true;
    }
    if(CustomerPageConstans.CREATEDATETIME.equalsIgnoreCase(filed)){
      return true;
    }
    if(CustomerPageConstans.CREATEUSER.equalsIgnoreCase(filed)){
      return true;
    }
    return false;
  }

  /**
   * 
   * <strong>Title : getInsertSql<br></strong>
   * <strong>Description : </strong>获取保存对象的sql<br> 
   * <strong>Create on : 2016-11-17 下午6:32:32<br></strong>
   * <p>
   * @param tableColumns
   * @param tableName
   * @return
   * String
   * @throws 
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   * <strong>修改历史:</strong><br>
   * 修改人 | 修改日期 | 修改描述<br>
   * -------------------------------------------<br>
   * <br>
   * <br>
   */
  private String getInsertSql(List<Map<String, Object>> tableColumns, 
      Map<String, Object> params, String tableName) {
    //组装保存语句的前半段(ID,...
    StringBuilder sql = new StringBuilder("insert into ");
    //组装参数  values 的后半段 (#{ID},...)
    StringBuilder parameters = new StringBuilder("(");
    sql.append(tableName).append("(");
    for (Map<String, Object> columns : tableColumns) {
      for (Entry<String, Object> entity : columns.entrySet()) {
        if (entity.getValue() != null) {
          if (params.containsKey(entity.getValue().toString().toUpperCase())) {
            sql.append(entity.getValue()).append(",");
            parameters.append("#{").append(entity.getValue()).append("}").append(",");
          }
        }
        break; //每个map就一个值
      }
    }
    sql.append(") values ").append(parameters.toString()).append(")");
    return sql.toString().replaceAll(",\\)", "\\)");
  }

  /**
   * 
   * <strong>Title : convertData<br></strong>
   * <strong>Description : 还原数据类型，界面传过来的都是字符串，要转换为相关的日期等</strong><br> 
   * <strong>Create on : 2016-11-17 下午6:15:04<br></strong>
   * <p>
   * @param value
   * @param dataType
   * @return
   * Object
   * @throws 
   * @author lidapeng<br>
   * @version <strong>v1.0.0</strong><br>
   * <br>
   * <strong>修改历史:</strong><br>
   * 修改人 | 修改日期 | 修改描述<br>
   * -------------------------------------------<br>
   * <br>
   * <br>
   * @throws ParseException 
   */
  private Object convertData(Object value, String dataType) throws ParseException {
    if (value != null) {
      if (dataType.equalsIgnoreCase("date")) {
        return DateUtils.getDateUtils(DateUtils.YYYY_MM_DD).parse(value.toString());
      } else if (dataType.equalsIgnoreCase("datetime")) {
        return DateUtils.getDateUtils().parse(value.toString());
      } else if (dataType.equalsIgnoreCase("integer")) {
        return Integer.valueOf(value.toString());
      } else if (dataType.equalsIgnoreCase("number")) {
        return Double.valueOf(value.toString());
      } else {
        return value.toString();
      }
    }
    return null;
  }
}
